OpenXR Extension Api Layer实现方案
OpenXR中的Extension有两种实现方式,第一种方式是直接以Hard Code
方式直接写在Runtime中,第二种方式则是以Api layer的方式来“外挂实现”。这边主要介绍api layer的实现方式为主。
Api Layer简介
Khronos在介绍Api Layer的时候给出的例子有点过于具体了,以至于刚开始我也就认为Api layer是实现debug + validating。
Some examples of features that API layers may expose include:
- Validating API usage
- Adding the ability to perform API tracing and debugging
- Intercept and filter information between the application and the runtime
但实际上,在整个Api layer介绍的开头,Khronos已经是给出了一个定义:
API layers are optional components that augment the OpenXR system.
然后也给出了一个功能和架构的介绍:
They may intercept, evaluate, modify, and insert existing OpenXR commands on their way from the application down to the runtime.
最后是给出了一个实施的建议:
API layers are implemented as libraries that are enabled in a variety of ways (including by application request).
以上这一大段的内容出自:https://www.khronos.org/registry/OpenXR/specs/1.0/loader.html#openxr-api-layers
其实概括起来,api layer是作为Runtime的一个增强组件而存在,主要是以so库的形式给openxr command提供一些额外的能力,这些能力可以是debug信息,validating,甚至是hook。有了以上的认知,我们再来看整个api layer中的call flow,感觉就会好一些:
Api Layer的实现
Api Layer的实现分为两个步骤,一个是业务/功能代码编写,第二个是loader对于so库的加载。
Api Layer的代码编写
Khronos本身针对api layer其实是有写了sample code,但是这个sample code具有一定的迷惑性,因为它是做了debug和validating两个样例。
github的下载地址:https://github.com/KhronosGroup/OpenXR-SDK-Source
当我们克隆这份代码以后,有一个比较简单的方式来编译,就是借助hello_xr
的工程来做api layer的编译。
为了避免歧义,我这边以commit:915196a27819257b513aae85985a129715d8cb7e为例来进行整个流程的演示:
https://github.com/KhronosGroup/OpenXR-SDK-Source/tree/915196a27819257b513aae85985a129715d8cb7e
api layer的编译
由于我们是借助hello_xr
的项目来进行编译,所以先要确定如何把api layer编进去,所以需要先去api_layers目录下找到具体的target
:
|
|
得到了对应的target:XrApiLayer_core_validation
,再回到hello_xr
的build.gradle
中,我们需要增加该target
:
|
|
需要打开'-DBUILD_API_LAYERS=ON'
,以及增加target:XrApiLayer_core_validation
这样以后直接编译hello_xr
,就可以了连带编译出libXrApiLayer_core_validation.so
,这样可以达到api layer编译的目的了。
业务代码的编写
由于实际业务中,我们是想要支持某个extension,而这个extension的实现并不是在runtime中做的,比如说支持XR_EXT_hand_tracking
,那么我们就可以在api layer中去实现xrCreateHandTrackerEXT
等等的函数,其他的特性,诸如XR_FB_passthrough
等等的也是同理。
假定我们需要通过api layer去实现XR_EXT_hand_tracking
,我们借用XrApiLayer_core_validation
的代码来简单实现:xrCreateHandTrackerEXT
。
- 首先是修改
validation_layer_generator.py
,在这里增加我们想要hook的函数:xrCreateHandTrackerEXT
|
|
在增加了这个代码后,编译过程中会生成:
OpenXR-SDK-Source/src/tests/hello_xr/.cxx/cmake/OpenGLESDebug/arm64-v8a/src/api_layers/xr_generated_core_validation.cpp
|
|
接着再来看一下对于这支api的使用:
123456789LAYER_EXPORT XrResult xrNegotiateLoaderApiLayerInterface(const XrNegotiateLoaderInfo *loaderInfo, const char * /*apiLayerName*/,XrNegotiateApiLayerRequest *apiLayerRequest) {.......apiLayerRequest->getInstanceProcAddr = reinterpret_cast<PFN_xrGetInstanceProcAddr>(GenValidUsageXrGetInstanceProcAddr);apiLayerRequest->createApiLayerInstance =reinterpret_cast<PFN_xrCreateApiLayerInstance>(CoreValidationXrCreateApiLayerInstance);.......return XR_SUCCESS;}本身
getInstanceProcAddr
的作用就是寻找库内
的函数指针,所以通过这个函数,我们就可以找到api layer so中的某个函数了。最后是需要实现一下:
CoreValidationXrCreateHandTrackerEXT
,具体的实现可以放在:1234567XRAPI_ATTR XrResult XRAPI_CALL CoreValidationXrCreateHandTrackerEXT(XrSession session,const XrHandTrackerCreateInfoEXT* createInfo,XrHandTrackerEXT* handTracker) {ALOGE("%s,%s,%d",__FILE__,__func__,__LINE__);return XR_SUCCESS;}根据call flow,如果需要继续往下调用到runtime或者是其他的api layer的部分,那么代码就要改成:
1234567XRAPI_ATTR XrResult XRAPI_CALL CoreValidationXrCreateHandTrackerEXT(XrSession session,const XrHandTrackerCreateInfoEXT* createInfo,XrHandTrackerEXT* handTracker) {ALOGE("%s,%s,%d",__FILE__,__func__,__LINE__);return GenValidUsageNextXrCreateHandTrackerEXT(session, createInfo, handTracker);}其中
GenValidUsageNextXrCreateHandTrackerEXT
的代码实际也是通过py文件生成的(这里可以不做考虑):OpenXR-SDK-Source/src/tests/hello_xr/.cxx/cmake/OpenGLESDebug/arm64-v8a/src/api_layers/xr_generated_core_validation.cpp
1234567891011121314151617181920212223242528815 XRAPI_ATTR XrResult XRAPI_CALL GenValidUsageNextXrCreateHandTrackerEXT(28816 XrSession session,28817 const XrHandTrackerCreateInfoEXT* createInfo,28818 XrHandTrackerEXT* handTracker) {28819 XrResult result = XR_SUCCESS;28820 try {28821 auto info_with_instance = g_session_info.getWithInstanceInfo(session);28822 GenValidUsageXrHandleInfo *gen_session_info = info_with_instance.first;28823 (void)gen_session_info; // quiet warnings28824 GenValidUsageXrInstanceInfo *gen_instance_info = info_with_instance.second;28825 result = gen_instance_info->dispatch_table->CreateHandTrackerEXT(session, createInfo, handTracker);28826 if (XR_SUCCESS == result && nullptr != handTracker) {28827 std::unique_ptr<GenValidUsageXrHandleInfo> handle_info(new GenValidUsageXrHandleInfo());28828 handle_info->instance_info = gen_instance_info;28829 handle_info->direct_parent_type = XR_OBJECT_TYPE_SESSION;28830 handle_info->direct_parent_handle = MakeHandleGeneric(session);28831 g_handtrackerext_info.insert(*handTracker, std::move(handle_info));28832 }28833 } catch (std::bad_alloc&) {28834 result = XR_ERROR_OUT_OF_MEMORY;28835 } catch (...) {28836 result = XR_ERROR_VALIDATION_FAILURE;28837 }28838 return result;28839 }
所以,对于xrCreateHandTrackerEXT
的最后中期望,是可以打印出:
|
|
Loader对于api layer的加载
在loader
的章节中,我们也知道api layer so需要被loader找到的话,需要写入对应的json文件。
这个部分可以参考:https://www.khronos.org/registry/OpenXR/specs/1.0/loader.html#api-layer-discovery, 其中的Example 7的图。
所以,针对这个情况,我们在准备api layer加载的时候,就需要准备:
- 对应的json文件
- 上面api layer编译出来的具体so文件
另外,对于api layer来说,又有两个类型:Implicit
和Explicit
,简单来说,Implicit
对于应用开发者来说是透明的,应用开发者可以认为”就是”Runtime提供的;而Explicit
是需要应用开发者在XrInstanceCreateInfo
中带入,是应用开发者明确需要使用的api layer。
这个部分的内容可以参考:https://www.khronos.org/registry/OpenXR/specs/1.0/loader.html#implicit-vs-explicit-api-layers
因此,针对xrCreateHandTrackerEXT
,我们倾向于使用Implicit
来实现,以下就是针对这个Implicit
的具体做法。
json文件的编写
- XrApiLayer.json
|
|
其中比较重要的参数是:
- “library_path”: “/data/app/libXrApiLayer_core_validation.so”
- “disable_environment”: “DISABLE_XR_API_LAYER_TEST_1”
- “name”: “XR_EXT_example”
其中name
的部分最后会是support extension
,出现在Available Extensions
中。
library_path
的部分,是指定了对应的api layer so库的具体地址
disable_environment
根据spec的定义,在Implicit
的api layer中是一个必选字段,代表的含义:Indicates an environment variable used to disable the implicit API layer.
如果只是demo的话,照抄上述的内容就可以了。
Loader的修改
实测下来,标准Khronos给出来的loader在android上并不会发现api layer,因此需要针对loader的部分做一些小修改。
|
|
实际就是hard code,把implicit json
文件放到了:/system/etc/openxr/1/api_layers/implicit.d/XrApiLayer.json
可以修改的更加漂亮,但是我们这边以便捷为主,直接这样写了,而QCOM的OpenXR方案中,实际也是写死的,因为从release出来的文件来看,QCOM方案下需要把json文件放到:/system/etc/openxr/1/api_layers/implicit.d/
工程验证
在完成了以上的任务后,我们会得到三个交付件:
- hello_xr.apk
- 这个apk可以使用adb install进行安装
- libXrApiLayer_core_validation.so
- push到
/data/app/libXrApiLayer_core_validation.so
- push到
- XrApiLayer.json
- push到
/system/etc/openxr/1/api_layers/implicit.d/XrApiLayer.json
- push到
接着就可以使用OpenXR Runtime(Monado方案)进行验证了,可以得到如下的信息:
支持的额外扩展:XR_EXT_example”
app端samplecode:
1234PFN_xrCreateHandTrackerEXT pfn_xrCreateHandTrackerEXT = nullptr;CHECK_XRCMD(xrGetInstanceProcAddr(m_instance, "xrCreateHandTrackerEXT",reinterpret_cast<PFN_xrVoidFunction*>(&pfn_xrCreateHandTrackerEXT)));pfn_xrCreateHandTrackerEXT(XR_NULL_HANDLE,nullptr,nullptr);api layer的hook打印